/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    mjpeg2rgb.c
 * @brief   转换视频数据格式，把摄像头原始的mjpeg格式转换成rgb565格式
 * @author  K
 * @version 0.0.1
 * @date    2021-07-26
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-07-27   | v0.0.1    | Kcode   | 转换视频数据格式
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <linux/videodev2.h>
#include <convert_manager.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <jpeglib.h>

#include "debug_manager.h"

typedef struct MyErrorMgr
{
	struct jpeg_error_mgr pub;
	jmp_buf setjmp_buffer;
}T_MyErrorMgr, *PT_MyErrorMgr;

extern void jpeg_mem_src_tj(j_decompress_ptr cinfo,
	      unsigned char * inbuffer, unsigned long insize);

/*!
 * @brief  判断输入输出格式是否为mhpeg和rgb
 * @return  0：不支持 1：支持
 */
static int isSupportMjpeg2Rgb(int pixel_format_in, int pixel_format_out)
{
    if (pixel_format_in != V4L2_PIX_FMT_MJPEG)
        return 0;
    if ((pixel_format_out != V4L2_PIX_FMT_RGB565) && \
    (pixel_format_out != V4L2_PIX_FMT_RGB32))
        return 0;

    return 1;
}

/*!
 * @brief  判断输入输出格式是否为mhpeg和rgb
 * @return  0：不支持 1：支持
 */
static void MyErrorExit(j_common_ptr ptCInfo)
{
    static char errStr[JMSG_LENGTH_MAX];
    
	PT_MyErrorMgr ptMyErr = (PT_MyErrorMgr)ptCInfo->err;

    /* Create the message */
    (*ptCInfo->err->format_message) (ptCInfo, errStr);
    DBG_PRINTF("%s\n", errStr);

	longjmp(ptMyErr->setjmp_buffer, 1);
}

/*!
 * @brief  判断输入输出格式是否为mhpeg和rgb
 * @return  0：不支持 1：支持
 */
static int CovertOneLine(int width, int SrcBpp, int DstBpp, 
            unsigned char *pSrcDatas, unsigned char *pDstDatas)
{
	unsigned int red;
	unsigned int green;
	unsigned int blue;
	unsigned int color;

	unsigned short *pDstDatas16bpp = (unsigned short *)pDstDatas;
	unsigned int   *pDstDatas32bpp = (unsigned int *)pDstDatas;

	int i;
	int pos = 0;

	if (SrcBpp != 24)
	{
		return -1;
	}

	if (DstBpp == 24)
	{
		memcpy(pDstDatas, pSrcDatas, width*3);
	}
	else
	{
		for (i = 0; i < width; i++)
		{
			red   = pSrcDatas[pos++];
			green = pSrcDatas[pos++];
			blue  = pSrcDatas[pos++];
			if (DstBpp == 32)
			{
				color = (red << 16) | (green << 8) | blue;
				*pDstDatas32bpp = color;
				pDstDatas32bpp++;
			}
			else if (DstBpp == 16)
			{
				/* 565 */
				red   = red >> 3;
				green = green >> 2;
				blue  = blue >> 3;
				color = (red << 11) | (green << 5) | (blue);
				*pDstDatas16bpp = color;
				pDstDatas16bpp++;
			}
		}
	}
	return 0;
}

/*!
 * @brief  支持的格式转换mjpeg为输出格式rgb
 *         mjpeg实际是上多个jpeg
 * @return  0：转换成功 -1：分配内存失败
 */
static int 
    Mjpeg2RgbConvert(PT_VIDEOBUF videobuf_in, PT_VIDEOBUF videobuf_out)

{
    struct jpeg_decompress_struct tDInfo;
    //struct jpeg_error_mgr tJErr;
    int ret;
    int RowStride;
    unsigned char *LineBuffer = NULL;
    unsigned char *pDest;
    T_MyErrorMgr tJerr;
    PT_PIXELDATAS pixeldatas_out = &videobuf_out->pixel_data;

    // 分配和初始化一个decompression结构体
    //tDInfo.err = jpeg_std_error(&tJErr);

    tDInfo.err               = jpeg_std_error(&tJerr.pub);
    tJerr.pub.error_exit     = MyErrorExit;

    if(setjmp(tJerr.setjmp_buffer))
    {
        /* 如果程序能运行到这里, 表示JPEG解码出错 */
        jpeg_destroy_decompress(&tDInfo);
        if (LineBuffer)
        {
            free(LineBuffer);
        }
        if (pixeldatas_out->PixelDatas)
        {
            free(pixeldatas_out->PixelDatas);
        }
        return -1;
    }

    jpeg_create_decompress(&tDInfo);

    // 把数据源设为内存中的数据
    jpeg_mem_src_tj(&tDInfo, videobuf_in->pixel_data.PixelDatas, 
                                videobuf_in->pixel_data.TotalBytes);

    ret = jpeg_read_header(&tDInfo, TRUE);

    // 设置解压参数,比如放大、缩小
    tDInfo.scale_num = tDInfo.scale_denom = 1;
    
    // 启动解压：jpeg_start_decompress 
    jpeg_start_decompress(&tDInfo);
    
    // 一行的数据长度
    RowStride = tDInfo.output_width * tDInfo.output_components;
    LineBuffer = malloc(RowStride);

    if (NULL == LineBuffer)
    {
        return -1;
    }

    pixeldatas_out->width  = tDInfo.output_width;
    pixeldatas_out->height = tDInfo.output_height;
    //pixeldatas_out->bpp    = bpp;
    pixeldatas_out->linebytes    = pixeldatas_out->width * pixeldatas_out->bpp / 8;
    pixeldatas_out->TotalBytes   = pixeldatas_out->height * pixeldatas_out->linebytes;
    
    if (NULL == pixeldatas_out->PixelDatas)
    {
        pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);
        if (pixeldatas_out->PixelDatas == NULL)
            return -1;
    }

    pDest = pixeldatas_out->PixelDatas;

    // 循环调用jpeg_read_scanlines来一行一行地获得解压的数据
    while (tDInfo.output_scanline < tDInfo.output_height) 
    {
        /* 得到一行数据,里面的颜色格式为0xRR, 0xGG, 0xBB */
        (void) jpeg_read_scanlines(&tDInfo, &LineBuffer, 1);

        // 转到ptPixelDatas去
        CovertOneLine(pixeldatas_out->width, 24, pixeldatas_out->bpp, LineBuffer, pDest);
        pDest += pixeldatas_out->linebytes;
    }
    
    free(LineBuffer);
    jpeg_finish_decompress(&tDInfo);
    jpeg_destroy_decompress(&tDInfo);

    return 0;
}


/*!
 * @brief  退出函数，释放内存
 * @return  0
 */
static int Mjpeg2RgbConvertExit(PT_VIDEOBUF videobuf_out)
{
    if (videobuf_out->pixel_data.PixelDatas == NULL)
    {
        free(videobuf_out->pixel_data.PixelDatas);
        videobuf_out->pixel_data.PixelDatas = NULL;
    }

    return 0;
}

static T_VIDEOCONVERTOPR s_Mjpeg2RgbConvert = {
    .name = "mjpeg2rgb",
    .isSupport   = isSupportMjpeg2Rgb,
    .Convert     = Mjpeg2RgbConvert,
    .ConvertExit = Mjpeg2RgbConvertExit,
};

/*!
 * @brief  初始化函数，把对支持的各个转换格式进行初始化工作
 *         对应的结构体放入链表统一管理
 * @return  0：初始化成功 1：失败
 */
int Mjpeg2RgbInit(void)
{
    return RegisterVideoConvertOpr(&s_Mjpeg2RgbConvert);
}

